package com.panopset.game.blackjack;

import java.util.StringTokenizer;

/**
 * Here we have narrowed a situation down to a basic strategy line.
 *
 * @author Karl Dinwiddie
 *
 */
public class StrategyLine implements Comparable<StrategyLine> {

    private final BlackjackConfig c;

    /**
     * Plain text source for strategy line.
     */
    private final String src;

    /**
     * Strategy line texts.
     */
    private final String[] sa;

    /**
     *
     * @param order Sort order.
     * @param source Strategy line.
     * @param config BlackjackConfig.
     */
    public StrategyLine(final Integer order, final String source,
            final BlackjackConfig config) {
        sortOrder = order;
        src = source;
        c = config;
        StringTokenizer st = new StringTokenizer(src, " ");
        sa = new String[st.countTokens()];
        int i = 0;
        while (st.hasMoreElements()) {
            sa[i++] = st.nextToken();
        }
        if (sa.length > 0) {
            key = sa[0];
        } else {
            key = "";
        }
    }

    /**
     *
     * @return Line source.
     */
    public String getSource() {
        return src;
    }

    private boolean canSurrender(final Situation s) {
        if (c.isLateSurrenderAllowed() && s.getHand().isInitialDeal()) {
            return true;
        }
        return false;
    }
    /**
     *
     * @param s
     *            Situation.
     * @return recommended action for given situation, based on strategy table.
     */
    public RecommendedAction getAction(final Situation s) {
        if (s == null || s.d == null) {
            return new RecommendedAction(BlackjackGame.CMD_DEAL, "L");
        }
        String text = getTextForDealerUpCard(s.d);

        final boolean singleDeck = s.getConfig().getDecks() == 1;
        Hand hand = s.getHand();
        if (hand == null || hand.isEmpty()) {
            return new RecommendedAction(BlackjackGame.CMD_DEAL, "L");
        }
        boolean canDouble = false;
        if (hand.isInitialDeal()) {
            if (hand.hasSplit) {
                canDouble = s.getConfig().isDoubleAfterSplitAllowed();
            } else {
                canDouble = true;
            }
        }
        if ("H".equals(text)) {
            return new RecommendedAction(BlackjackGame.CMD_HIT, text);
        } else if ("H*".equals(text)) {
            if (hand.isSplitAce()) {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
            if (singleDeck && canDouble) {
                return new RecommendedAction(BlackjackGame.CMD_DOUBLE, text);
            } else {
                return new RecommendedAction(BlackjackGame.CMD_HIT, text);
            }
        } else if ("Hx".equals(text)) {
            if (hand.isSplitAce()) {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
            if (singleDeck && canDouble) {
                return new RecommendedAction(BlackjackGame.CMD_DOUBLE, text);
            } else {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
        } else if ("Hd".equals(text)) {
            if (!canDouble) {
                return new RecommendedAction(BlackjackGame.CMD_HIT, text);
            }
            if (!c.isDealerHitSoft17() && c.getDecks() > 2) {
                return new RecommendedAction(BlackjackGame.CMD_HIT, text);
            }
            return new RecommendedAction(BlackjackGame.CMD_DOUBLE, text);
        } else if ("H@".equals(text)) {
            if (hand.isSplitAce()) {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
            if (singleDeck && hand.isSplitable(c)) {
                return new RecommendedAction(BlackjackGame.CMD_SPLIT, text);
            } else {
                return new RecommendedAction(BlackjackGame.CMD_HIT, text);
            }
        } else if ("HT".equals(text)) {
            if (hand.isInitialDeal()) {
                for (BlackjackCard card : hand.cards) {
                    if (card.getHardValue() == 10) {
                        return new RecommendedAction(BlackjackGame.CMD_HIT, text);
                    }
                }
            }
            return new RecommendedAction(BlackjackGame.CMD_STAND, text);
        } else if ("Rs".equals(text)) {
            if (canSurrender(s)) {
                return new RecommendedAction(BlackjackGame.CMD_SURRENDER, text);
            }
            return new RecommendedAction(BlackjackGame.CMD_STAND, text);
        } else if ("Rh".equals(text)) {
            if (canSurrender(s)) {
                return new RecommendedAction(BlackjackGame.CMD_SURRENDER, text);
            }
            return new RecommendedAction(BlackjackGame.CMD_HIT, text);
        } else if ("Rp".equals(text)) {
            if (canSurrender(s)) {
                return new RecommendedAction(BlackjackGame.CMD_SURRENDER, text);
            }
            return new RecommendedAction(BlackjackGame.CMD_SPLIT, text);
        } else if ("R*".equals(text)) {
            if (canSurrender(s)) {
                return new RecommendedAction(BlackjackGame.CMD_SURRENDER, text);
            }
            for (BlackjackCard card : hand.cards) {
                if (card.getHardValue() == 4 || card.getHardValue() == 5) {
                    return new RecommendedAction(BlackjackGame.CMD_STAND, text);
                }
            }
            return new RecommendedAction(BlackjackGame.CMD_HIT, text);
        } else if ("Dh".equals(text)) {
            if (hand.isSplitAce()) {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
            if (canDouble) {
                return new RecommendedAction(BlackjackGame.CMD_DOUBLE, text);
            } else {
                return new RecommendedAction(BlackjackGame.CMD_HIT, text);
            }
        } else if ("Ds".equals(text)) {
            if (hand.isSplitAce()) {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
            if (canDouble) {
                return new RecommendedAction(BlackjackGame.CMD_DOUBLE, text);
            } else {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
        } else if ("P".equals(text)) {
            if (hand.isSplitAce() && !c.isResplitAcesAllowed()) {
                return new RecommendedAction(BlackjackGame.CMD_STAND, text);
            }
            return new RecommendedAction(BlackjackGame.CMD_SPLIT, text);
        } else if ("R".equals(text)) {
            return new RecommendedAction(BlackjackGame.CMD_SURRENDER, text);
        } else if ("S".equals(text)) {
            return new RecommendedAction(BlackjackGame.CMD_STAND, text);
        } else {
            return new RecommendedAction(BlackjackGame.CMD_STAND, text);
        }
    }

    private String getTextForDealerUpCard(BlackjackCard card) {
        if (sa == null || sa.length < 11) {
            return "";
        }
        return sa[card.getSoftValue() - 1];
    }

    private final Integer sortOrder;

    private final String key;

    public String getKey() {
        return key;
    }

    @Override
    public String toString() {
        return getSource();
    }

    @Override
    public int compareTo(final StrategyLine o) {
        return sortOrder.compareTo(o.sortOrder);
    }
}
